#if defined(ARCTIC)
/****************************************************************
 *
 * Header Files
 *
 ***************************************************************/
#include "sal.h"
#include "ctc_error.h"
#include "ctc_macro.h"
#include "ctc_lb_hash.h"
#include "sys_usw_common.h"
#include "sys_usw_lb_hash.h"
#include "drv_api.h"

#define SYS_HASH_PACKET_TYPE_MAPPING_NUM_MAX 8
#define SYS_HASH_PACKET_TYPE_NUM 64
#define SYS_HASH_MASK_PROFILE_NUM 53
#define SYS_HASH_SEED1_BULK_POS 14
#define SYS_HASH_SEED2_BULK_POS 15
#define SYS_HASH_IP_SUB_TYPE_NUM 3
#define SYS_HASH_MPLS_SUB_TYPE_NUM 5
#define SYS_HASH_VXLAN_SUB_TYPE_NUM 3
#define SYS_HASH_GRE_SUB_TYPE_NUM 5
#define SYS_LB_HASH_CFG_NUM 5

#define SYS_LB_HASH_MODE_CHECK()\
    do\
    {\
        if (SYS_LB_HASH_IS_COMPATIBLE_MODE)\
        {\
            return CTC_E_NOT_SUPPORT;\
        }\
    }while(0)

#define SYS_LB_HASH_COMPATIBLE_IOR(lchip, sel_id, hash_ctl) \
do \
{ \
    uint8 _sel_id = SYS_LB_HASH_IS_COMPATIBLE_MODE ? (sel_id)*2 : (sel_id); \
    uint8 _step = IpeHashCtl1_t - IpeHashCtl0_t; \
    uint32 _cmd = DRV_IOR(IpeHashCtl0_t + _sel_id * _step, DRV_ENTRY_FLAG); \
    CTC_ERROR_RETURN(DRV_IOCTL((lchip), 0, _cmd, &(hash_ctl))); \
}while(0)

#define SYS_LB_HASH_COMPATIBLE_IOW(lchip, sel_id, hash_ctl) \
do \
{ \
    uint8 _sel_id = SYS_LB_HASH_IS_COMPATIBLE_MODE ? (sel_id)*2 : (sel_id); \
    uint8 _step = IpeHashCtl1_t - IpeHashCtl0_t; \
    uint32 _cmd = DRV_IOW(IpeHashCtl0_t + _sel_id * _step, DRV_ENTRY_FLAG); \
    CTC_ERROR_RETURN(DRV_IOCTL((lchip), 0, _cmd, &(hash_ctl))); \
    if (SYS_LB_HASH_IS_COMPATIBLE_MODE) \
    { \
        _cmd = DRV_IOW(IpeHashCtl0_t + _sel_id * _step + 1, DRV_ENTRY_FLAG); \
        CTC_ERROR_RETURN(DRV_IOCTL((lchip), 0, _cmd, &(hash_ctl))); \
    } \
}while(0)

struct sys_hash_cfg_data_s
{
    uint32 fields;
    uint8 ctrl_type[SYS_LB_HASH_CFG_NUM];
    uint8 ctrl_value[SYS_LB_HASH_CFG_NUM];
    uint8 ctrl_cnt;
    uint8 field_type[SYS_LB_HASH_CFG_NUM];
    uint8 profile_type[SYS_LB_HASH_CFG_NUM];
    uint8 type_cnt;
    uint8 sel_id;
};
typedef struct sys_hash_cfg_data_s sys_hash_cfg_data_t;

struct sys_hash_field_info_s
{
    uint16 vni_en        : 1;
    uint16 flow_label_en : 1;
    uint16 udf_field_en  : 1;
    uint16 l4_port_en    : 1;
    uint16 l4_protocol_en: 1;
    uint16 label_en      : 1;
    uint16 mac_en        : 1;
    uint16 vlan_en       : 1;
    uint16 is_inner      : 1;
    uint16 only_l2       : 1;
    uint16 rsv           : 6;
};
typedef struct sys_hash_field_info_s sys_hash_field_info_t;

enum sys_hash_field_type_e
{
    SYS_HASH_FIELD_TYPE_L2,
    SYS_HASH_FIELD_TYPE_IP,
    SYS_HASH_FIELD_TYPE_MPLS,
    SYS_HASH_FIELD_TYPE_VXLAN,
    SYS_HASH_FIELD_TYPE_GRE,
    SYS_HASH_FIELD_TYPE_NVGRE,
    SYS_HASH_FIELD_TYPE_TRILL,
    SYS_HASH_FIELD_TYPE_FCOE
};
typedef enum sys_hash_field_type_e sys_hash_field_type_t;

enum sys_hash_fwd_type_e
{
    SYS_HASH_FWD_TYPE_RESERVE = 0,
    SYS_HASH_FWD_TYPE_FROM_CFLEX,
    SYS_HASH_FWD_TYPE_IPV4_VXLAN,
    SYS_HASH_FWD_TYPE_IPV4_NVGRE,
    SYS_HASH_FWD_TYPE_IPV4_GRE,
    SYS_HASH_FWD_TYPE_IPV4_INNER_IPV4,
    SYS_HASH_FWD_TYPE_IPV4_INNER_IPV6,
    SYS_HASH_FWD_TYPE_IPV4_UDP_TUNNEL,
    SYS_HASH_FWD_TYPE_IPV6_VXLAN,
    SYS_HASH_FWD_TYPE_IPV6_NVGRE,
    SYS_HASH_FWD_TYPE_IPV6_GRE,
    SYS_HASH_FWD_TYPE_IPV6_INNER_IPV6,
    SYS_HASH_FWD_TYPE_IPV6_INNER_IPV4,
    SYS_HASH_FWD_TYPE_IPV6_UDP_TUNNEL,
    SYS_HASH_FWD_TYPE_MPLS_INNER_IPV4,
    SYS_HASH_FWD_TYPE_MPLS_INNER_IPV6,
    SYS_HASH_FWD_TYPE_MPLS_INNER_OTHER,
    SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV6,
    SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV4,
    SYS_HASH_FWD_TYPE_MPLS_VPWS,
    SYS_HASH_FWD_TYPE_MPLS_VPLS,
    SYS_HASH_FWD_TYPE_FCOE,
    SYS_HASH_FWD_TYPE_TRILL_DECAP,
    SYS_HASH_FWD_TYPE_TRILL_TRANSIT,
    SYS_HASH_FWD_TYPE_IPV4,
    SYS_HASH_FWD_TYPE_IPV6,
    SYS_HASH_FWD_TYPE_BRIDGE_IPV4,
    SYS_HASH_FWD_TYPE_BRIDGE_IPV6,
    SYS_HASH_FWD_TYPE_BRIDGE,
    SYS_HASH_FWD_TYPE_MPLS,
    SYS_HASH_FWD_TYPE_ALL
};
typedef enum sys_hash_fwd_type_e sys_hash_fwd_type_t;

extern int32 _sys_tsingma_hash_config_set_hash_control(uint8 lchip, ctc_lb_hash_config_t* p_hash_cfg);
extern int32 _sys_tsingma_hash_config_get_hash_control(uint8 lchip, ctc_lb_hash_config_t* p_hash_cfg);

/* hash matrix generate function, input poly, output matrix */
int32
_sys_at_hash_gen_matrix(uint8 poly, uint32* matrix) 
{
    uint16 data_pararrel_bit_length = 256;
    uint8 matrix_bin[16][16] = {{0}};
    uint32 matrix_hex[8] = {0};
    uint16 poly_hex = 0;
    uint8 poly_len = 0;
    int8 i = 0;
    int8 k = 0;
    int16 l = 0;

    switch (poly){
    case SYS_LB_HASHPOLY_CRC8_POLY1:
        poly_hex = 0x87;    /* CRC8 x8+x7+x2+x+1 */
        poly_len = 8;
        break;
    case SYS_LB_HASHPOLY_CRC16_POLY2:
        poly_hex = 0x39;    /* CRC16 x16+x5+x4+x3+1 */
        poly_len = 16;
        break;
    case SYS_LB_HASHPOLY_CRC16_POLY5:
        poly_hex = 0x8005;  /* CRC16_BISYNC x16+x15+x2+1 */
        poly_len = 16;
        break;
    case SYS_LB_HASHPOLY_CRC16_POLY6:
        poly_hex = 0x1021;  /* CRC16_CCITT x16+x12+x5+1 */
        poly_len = 16;
        break;
    default:
        return CTC_E_INIT_FAIL;
    }
    
    // init l = 0 case;
    for (i = 0; i < poly_len; i++ ) 
    {
        for (k = 0; k < poly_len; k++ ) 
        {
            matrix_bin[i][k] = (i == k) ? 1 : 0;
        }
    }
 
    // build matrix
    for (l = 1; l <= data_pararrel_bit_length; l++  ) 
    {
        for (k = 0; k < poly_len; k++ ) 
        {
            uint8 sign = matrix_bin[poly_len - 1][k]; 
            for (i = poly_len - 1; i >= 0; i-- ) 
            {
                if ( i == 0 ) 
                {
                    matrix_bin[i][k] = sign;
                }
                else 
                {
                    matrix_bin[i][k] = (poly_hex&(1 << i)) ? (matrix_bin[i - 1][k] ^ sign ) : matrix_bin[i - 1][k];
                }
            }
        }
    } 

    // convert to hex
    for (i = poly_len - 1; i >= 0; i--)
    {
        for (k = poly_len - 1; k >= 0; k--)
        {   
            matrix_hex[(i*poly_len + k) / 32] |= (matrix_bin[i][k] << (i*poly_len + k) % 32);
        }
    }

    sal_memcpy(matrix, matrix_hex, sizeof(matrix_hex));

    return CTC_E_NONE;
}

int32
sys_at_hash_init(uint8 lchip)
{
    IpeHashMergeCtl_m merge_ctl;
    CrcHashMatrixCtl_m hash_matrix;
    IpePreLookupCtl_m pre_hash_ctl;
    IpePreLookupHashMap_m pre_hash_map;
    IpeHashCtl0_m hash_ctl;
    IpeHashCrcSeedCtl_m hash_seed_ctl;

    uint32 matrix_value[8] = {0};
    uint32 cmd = 0;
    uint8 step1 = 0;
    uint8 i = 0;
    uint8 j = 0;

    step1 = IpeHashCtl1_t - IpeHashCtl0_t;

    /* init hash ctl */
    for (i = 0; i < SYS_USW_LB_HASH_CTL_NUM * 2; i++)
    {
        cmd = DRV_IOR(IpeHashCtl0_t + i * step1, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &hash_ctl));

        /* enable tunnel packet select l2 field independent */
        SetIpeHashCtl0(V, v4TunnelCtlEn_f, &hash_ctl, 1);
        SetIpeHashCtl0(V, v6TunnelCtlEn_f, &hash_ctl, 1);
        SetIpeHashCtl0(V, bulk12And13Ipv6UseFlowLabel0_f, &hash_ctl, 1);
        SetIpeHashCtl0(V, bulk12And13Ipv6UseFlowLabel1_f, &hash_ctl, 1);
        SetIpeHashCtl0(V, bulk12And13Ipv6UseFlowLabel2_f, &hash_ctl, 1);
        SetIpeHashCtl0(V, bulk12And13Ipv6UseFlowLabel4_f, &hash_ctl, 1);
        SetIpeHashCtl0(V, bulk12And13Ipv6UseFlowLabel5_f, &hash_ctl, 1);
        SetIpeHashCtl0(V, ipv4PayloadIsV6UseFlowLabel_f, &hash_ctl, 1);

        cmd = DRV_IOW(IpeHashCtl0_t + i * step1, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &hash_ctl));
    }

    /* init hash merge */
    cmd = DRV_IOR(IpeHashMergeCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &merge_ctl));
        
    SetIpeHashMergeCtl(V, supportMergePolyEn_f, &merge_ctl, 0x0CE4); /* bitmap for selector merge, indicate which hash poly support merge */
    cmd = DRV_IOW(IpeHashMergeCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &merge_ctl));

    /* init hash matrix, generate function refer to _sys_at_hash_gen_matrix */
    sal_memset(&hash_matrix, 0, sizeof(CrcHashMatrixCtl_m));
    cmd = DRV_IOR(CrcHashMatrixCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &hash_matrix));

    _sys_at_hash_gen_matrix(SYS_LB_HASHPOLY_CRC8_POLY1, matrix_value);
    SetCrcHashMatrixCtl(A, gCrc8_field_f, &hash_matrix, matrix_value);
    _sys_at_hash_gen_matrix(SYS_LB_HASHPOLY_CRC16_POLY2, matrix_value);
    SetCrcHashMatrixCtl(A, gCrc16_field_f, &hash_matrix, matrix_value);
    _sys_at_hash_gen_matrix(SYS_LB_HASHPOLY_CRC16_POLY5, matrix_value);
    SetCrcHashMatrixCtl(A, gCrc16Bisync_field_f, &hash_matrix, matrix_value);
    _sys_at_hash_gen_matrix(SYS_LB_HASHPOLY_CRC16_POLY6, matrix_value);
    SetCrcHashMatrixCtl(A, gCrc16Ccitt_field_f, &hash_matrix, matrix_value);

#if 0
    /* hash matrix value */
    matrix_value[1] = 0xC0201008;
    matrix_value[0] = 0x04828180;
    SetCrcHashMatrixCtl(A, gCrc8_field_f, &hash_matrix, matrix_value);
    matrix_value[7] = 0xC7FF2400;
    matrix_value[6] = 0x12000900;
    matrix_value[5] = 0x04800240;
    matrix_value[4] = 0x01200090;
    matrix_value[3] = 0x00480024;
    matrix_value[2] = 0x8012C009;
    matrix_value[1] = 0x6004B002;
    matrix_value[0] = 0x1FFE8FFF;
    SetCrcHashMatrixCtl(A, gCrc16Bisync_field_f, &hash_matrix, matrix_value);
    matrix_value[7] = 0x3A619D30;
    matrix_value[6] = 0x4E98274C;
    matrix_value[5] = 0x29C794E3;
    matrix_value[4] = 0xCA716538;
    matrix_value[3] = 0xB29CD94E;
    matrix_value[2] = 0xECA74C32;
    matrix_value[1] = 0xA619D30C;
    matrix_value[0] = 0xE98674C3;
    SetCrcHashMatrixCtl(A, gCrc16Ccitt_field_f, &hash_matrix, matrix_value);
    matrix_value[7] = 0xCF8067C0;
    matrix_value[6] = 0x33E099F0;
    matrix_value[5] = 0x4CF8A67C;
    matrix_value[4] = 0xD33EE99F;
    matrix_value[3] = 0xF4CF7A67;
    matrix_value[2] = 0x3D335119;
    matrix_value[1] = 0x670C7C06;
    matrix_value[0] = 0x3E039F01;
    SetCrcHashMatrixCtl(A, gCrc16_field_f, &hash_matrix, matrix_value);
#endif
   
    cmd = DRV_IOW(CrcHashMatrixCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &hash_matrix));

    /* if enable snooping, ip-tunnel packet be parsed as tunnel packet not ip packet, keep same with IpeHashCtl.transitNodeV4TunnelFormatPktHashMode */
    cmd = DRV_IOR(IpePreLookupCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &pre_hash_ctl));
    SetIpePreLookupCtl(V, transitNodeV4TunnelHashMode_f, &pre_hash_ctl, 0x3F);
    SetIpePreLookupCtl(V, transitNodeV6TunnelHashMode_f, &pre_hash_ctl, 0x3F);
    cmd = DRV_IOW(IpePreLookupCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &pre_hash_ctl));

    /* init IpePreLookupHashMap */
    step1 = IpePreLookupHashMap_g_1_hashMaskProfileId_f - IpePreLookupHashMap_g_0_hashMaskProfileId_f;
    for (i = 0; i < SYS_HASH_PACKET_TYPE_NUM / 8; i++)
    {
        cmd = DRV_IOR(IpePreLookupHashMap_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, i, cmd, &pre_hash_map));
        for (j = 0; j < 8; j++)
        {
            SetIpePreLookupHashMap(V, g_0_hashMaskProfileId_f + j*step1, &pre_hash_map, i*8+j);
        }
        cmd = DRV_IOW(IpePreLookupHashMap_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, i, cmd, &pre_hash_map));
    }
    sal_memset(&hash_seed_ctl, 0, sizeof(IpeHashCrcSeedCtl_m));
    for (i = 0; i < 8; i++)
    {
        SetIpeHashCrcSeedCtl(V, gHashGen_0_crc8Poly1Seed0_f + i, &hash_seed_ctl, 0xFF);
        SetIpeHashCrcSeedCtl(V, gHashGen_0_crc16BisyncSeed0_f + i, &hash_seed_ctl, 0xFFFF);
        SetIpeHashCrcSeedCtl(V, gHashGen_0_crc16CcittSeed0_f + i, &hash_seed_ctl, 0xFFFF);
        SetIpeHashCrcSeedCtl(V, gHashGen_0_crc16Poly1Seed0_f + i, &hash_seed_ctl, 0xFFFF);
        SetIpeHashCrcSeedCtl(V, gHashGen_0_crc32Poly1Seed_f + i, &hash_seed_ctl, 0xFFFFFFFF);
    }

    cmd = DRV_IOW(IpeHashCrcSeedCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &hash_seed_ctl));

    
    return CTC_E_NONE;
}

int32
sys_at_hash_set_selector_ext(uint8 lchip, void* p_hash_selector_ext)
{
    ctc_lb_hash_selector_ext_t* p_selector_ext = 0;
    IpeHashMergeCtl_m merge_ctl;
    int32 bit_cnt = 0;
    uint32 tmp_sel_bitmap = 0;
    uint32 cmd = 0;
    uint8 sel_id = 0;
    uint8 step1 = 0;
    uint8 step2 = 0;
    uint8 index = 0;

    /* selector merge do not support 4 selector mode */
    SYS_LB_HASH_MODE_CHECK();
    
    p_selector_ext = (ctc_lb_hash_selector_ext_t*) p_hash_selector_ext;
    CTC_PTR_VALID_CHECK(p_selector_ext);
    
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "sel_id = %d, sel_bitmap = 0x%.2x\n",
                       p_selector_ext->sel_grp_id, p_selector_ext->sel_grp_bitmap);
    
    CTC_VALUE_RANGE_CHECK(p_selector_ext->sel_grp_id, MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM),
        MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) + MCHIP_CAP(SYS_CAP_LB_HASH_EXT_SELECT_NUM) - 1);
    
    CTC_BMP_COUNT(p_selector_ext->sel_grp_bitmap, bit_cnt, 0, 8);
    if (bit_cnt != 0 && bit_cnt != 2)
    {
        return CTC_E_INVALID_PARAM;
    }

    cmd = DRV_IOR(IpeHashMergeCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &merge_ctl));
    
    sel_id = p_selector_ext->sel_grp_id - MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM);
    step1 = IpeHashMergeCtl_gHashMerger_1_enable_f - IpeHashMergeCtl_gHashMerger_0_enable_f;
    step2 = IpeHashMergeCtl_gHashMerger_1_setGeneratorId0_f - IpeHashMergeCtl_gHashMerger_0_setGeneratorId0_f;
    if (0 == bit_cnt)
    {
        SetIpeHashMergeCtl(V, gHashMerger_0_enable_f + sel_id*step1, &merge_ctl, 0);
    }
    else
    {
        SetIpeHashMergeCtl(V, gHashMerger_0_enable_f + sel_id*step1, &merge_ctl, 1);
        tmp_sel_bitmap = p_selector_ext->sel_grp_bitmap;

        CTC_BMP_FIRST_VALID(&tmp_sel_bitmap, MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM), index);
        SetIpeHashMergeCtl(V, gHashMerger_0_setGeneratorId0_f + sel_id*step2, &merge_ctl, index);
        CTC_BIT_UNSET(tmp_sel_bitmap, index);

        CTC_BMP_FIRST_VALID(&tmp_sel_bitmap, MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM), index);
        SetIpeHashMergeCtl(V, gHashMerger_0_setGeneratorId1_f + sel_id*step2, &merge_ctl, index);
    }
    
    cmd = DRV_IOW(IpeHashMergeCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &merge_ctl));
    
    return CTC_E_NONE;
}

int32
sys_at_hash_get_selector_ext(uint8 lchip, void* p_hash_selector_ext)
{
    ctc_lb_hash_selector_ext_t* p_selector_ext = 0;
    IpeHashMergeCtl_m merge_ctl;
    uint32 cmd = 0;
    uint8 merge_en = 0;
    uint8 sel_id_tmp = 0;
    uint8 sel_id = 0;
    uint8 step1 = 0;
    uint8 step2 = 0; 

    /* selector merge do not support 4 selector mode */
    SYS_LB_HASH_MODE_CHECK();
    
    p_selector_ext = (ctc_lb_hash_selector_ext_t*) p_hash_selector_ext;
    CTC_PTR_VALID_CHECK(p_selector_ext);
    CTC_VALUE_RANGE_CHECK(p_selector_ext->sel_grp_id, MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM), MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) + MCHIP_CAP(SYS_CAP_LB_HASH_EXT_SELECT_NUM) - 1);

    cmd = DRV_IOR(IpeHashMergeCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &merge_ctl));

    sel_id = p_selector_ext->sel_grp_id - MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM);
    step1 = IpeHashMergeCtl_gHashMerger_1_enable_f - IpeHashMergeCtl_gHashMerger_0_enable_f;
    step2 = IpeHashMergeCtl_gHashMerger_1_setGeneratorId0_f - IpeHashMergeCtl_gHashMerger_0_setGeneratorId0_f;
    
    merge_en = GetIpeHashMergeCtl(V, gHashMerger_0_enable_f + sel_id*step1, &merge_ctl);
    if(!merge_en)
    {
        p_selector_ext->sel_grp_bitmap = 0;
        return CTC_E_NONE;
    }

    sel_id_tmp = GetIpeHashMergeCtl(V, gHashMerger_0_setGeneratorId0_f + sel_id*step2, &merge_ctl);
    CTC_BIT_SET(p_selector_ext->sel_grp_bitmap, sel_id_tmp);
    sel_id_tmp = GetIpeHashMergeCtl(V, gHashMerger_0_setGeneratorId1_f + sel_id*step2, &merge_ctl);
    CTC_BIT_SET(p_selector_ext->sel_grp_bitmap, sel_id_tmp);
    
    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_packet_type_mapping(uint8 ctc_packet_type, uint8 sub_type, uint8* sys_packet_type, uint8* cnt)
{
    uint8 cnt_tmp = 0;

    switch (ctc_packet_type << CTC_CONST8 | sub_type)
    {
    case (CTC_LB_HASH_PKT_TYPE_L2 << CTC_CONST8 | 0):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_BRIDGE;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_FROM_CFLEX;
        break;
    case (CTC_LB_HASH_PKT_TYPE_IPV4 << CTC_CONST8 | 0):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_BRIDGE_IPV4;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_INNER_IPV4;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_INNER_IPV6;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_UDP_TUNNEL;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4;
        break;
    case (CTC_LB_HASH_PKT_TYPE_IPV4 << CTC_CONST8 | 1):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_INNER_IPV4;
        break;
    case (CTC_LB_HASH_PKT_TYPE_IPV4 << CTC_CONST8 | 2):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_INNER_IPV6;
        break;
    case (CTC_LB_HASH_PKT_TYPE_VXLAN << CTC_CONST8 | 0):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_VXLAN;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_VXLAN;
        break;
    case (CTC_LB_HASH_PKT_TYPE_VXLAN << CTC_CONST8 | 1):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_VXLAN;
        break; 
    case (CTC_LB_HASH_PKT_TYPE_VXLAN << CTC_CONST8 | 2):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_VXLAN;
        break;
    case (CTC_LB_HASH_PKT_TYPE_GRE << CTC_CONST8 | 0):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_GRE;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_GRE;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_NVGRE;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_NVGRE;
        break;
    case (CTC_LB_HASH_PKT_TYPE_GRE << CTC_CONST8 | 1):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_GRE;
        break;
    case (CTC_LB_HASH_PKT_TYPE_GRE << CTC_CONST8 | 2):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_GRE;
        break;
    case (CTC_LB_HASH_PKT_TYPE_GRE << CTC_CONST8 | 3):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV4_NVGRE;
        break;
    case (CTC_LB_HASH_PKT_TYPE_GRE << CTC_CONST8 | 4):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_NVGRE;
        break;
    case (CTC_LB_HASH_PKT_TYPE_IPV6 << CTC_CONST8 | 0):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_BRIDGE_IPV6;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_INNER_IPV4;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_INNER_IPV6;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_UDP_TUNNEL;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6;
        break;
    case (CTC_LB_HASH_PKT_TYPE_IPV6 << CTC_CONST8 | 1):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_INNER_IPV4;
        break;
    case (CTC_LB_HASH_PKT_TYPE_IPV6 << CTC_CONST8 | 2):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_IPV6_INNER_IPV6;
        break;
    case (CTC_LB_HASH_PKT_TYPE_MPLS << CTC_CONST8 | 0):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_INNER_OTHER;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_INNER_IPV4;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_INNER_IPV6;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_VPWS;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_VPLS;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV4;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV6;
        break;
    case (CTC_LB_HASH_PKT_TYPE_MPLS << CTC_CONST8 | 1):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV4;
        break;
    case (CTC_LB_HASH_PKT_TYPE_MPLS << CTC_CONST8 | 2):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV6;
        break;
    case (CTC_LB_HASH_PKT_TYPE_MPLS << CTC_CONST8 | 3):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_VPLS;
        break;
    case (CTC_LB_HASH_PKT_TYPE_MPLS << CTC_CONST8 | 4):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_MPLS_VPWS;
        break;
    case (CTC_LB_HASH_PKT_TYPE_TRILL << CTC_CONST8 | 0):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_TRILL_DECAP;
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_TRILL_TRANSIT;
        break;
    case (CTC_LB_HASH_PKT_TYPE_FCOE << CTC_CONST8 | 0):
        sys_packet_type[cnt_tmp++] = SYS_HASH_FWD_TYPE_FCOE;
        break;
    default:
        return CTC_E_INVALID_CONFIG;
        break;
    }
    
    *cnt = cnt_tmp;
    return CTC_E_NONE;
}

int32
sys_at_hash_set_selector_remap(uint8 lchip, void* p_hash_selector_remap)
{
    ctc_lb_hash_selector_remap_t* p_selector_remap = 0;
    IpeHashMergeBusSel0_m merge_bus;
    uint32 cmd = 0;
    uint8 packet_type[SYS_HASH_PACKET_TYPE_MAPPING_NUM_MAX] = {0};
    uint8 cnt = 0;
    uint8 step1 = 0;
    uint8 step2 = 0;
    uint8 i = 0;
    uint8 j = 0;

    /* selector merge do not support 4 selector mode */
    SYS_LB_HASH_MODE_CHECK();

    p_selector_remap = (ctc_lb_hash_selector_remap_t*)p_hash_selector_remap;
    CTC_PTR_VALID_CHECK(p_selector_remap);
    
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "packet_type = %d\n", p_selector_remap->packet_type);
    
    CTC_MAX_VALUE_CHECK(p_selector_remap->packet_type, CTC_LB_HASH_PKT_TYPE_FCOE);
    
    for (i = 0; i < MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM); i++)
    {
        CTC_MAX_VALUE_CHECK(p_selector_remap->sel_grp_id[i], MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM)+MCHIP_CAP(SYS_CAP_LB_HASH_EXT_SELECT_NUM)-1);
    }

    CTC_ERROR_RETURN(_sys_at_hash_packet_type_mapping(p_selector_remap->packet_type, p_selector_remap->sub_type, packet_type, &cnt));

    step1 = IpeHashMergeBusSel0_g_1_sel_f - IpeHashMergeBusSel0_g_0_sel_f;
    step2 = IpeHashMergeBusSel1_t - IpeHashMergeBusSel0_t;
    for (i = 0; i < MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM); i++)
    {
        cmd = DRV_IOR(IpeHashMergeBusSel0_t+i, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &merge_bus));
        for (j = 0; j < cnt; j++)
        {
            SetIpeHashMergeBusSel0(V, g_0_sel_f + packet_type[j]*step1, &merge_bus, p_selector_remap->sel_grp_id[i] + 1);
        }
        cmd = DRV_IOW(IpeHashMergeBusSel0_t+i*step2, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &merge_bus));
    }
    
    return CTC_E_NONE;  
}

int32
sys_at_hash_get_selector_remap(uint8 lchip, void* p_hash_selector_remap)
{
    ctc_lb_hash_selector_remap_t* p_selector_remap = 0;
    IpeHashMergeBusSel0_m merge_bus;
    uint32 cmd = 0;
    uint8 packet_type[SYS_HASH_PACKET_TYPE_MAPPING_NUM_MAX] = {0};
    uint8 cnt = 0;
    uint8 step1 = 0;
    uint8 step2 = 0;
    uint8 i = 0;

    /* selector merge do not support 4 selector mode */
    SYS_LB_HASH_MODE_CHECK();
	SYS_LB_HASH_INIT_CHECK();

    p_selector_remap = (ctc_lb_hash_selector_remap_t*)p_hash_selector_remap;
    CTC_PTR_VALID_CHECK(p_selector_remap);
    CTC_MAX_VALUE_CHECK(p_selector_remap->packet_type, CTC_LB_HASH_PKT_TYPE_FCOE);

    CTC_ERROR_RETURN(_sys_at_hash_packet_type_mapping(p_selector_remap->packet_type, p_selector_remap->sub_type, packet_type, &cnt));

    step1 = IpeHashMergeBusSel0_g_1_sel_f - IpeHashMergeBusSel0_g_0_sel_f;
    step2 = IpeHashMergeBusSel1_t - IpeHashMergeBusSel0_t;
    for (i = 0; i < MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM); i++)
    {
        cmd = DRV_IOR(IpeHashMergeBusSel0_t+i*step2, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &merge_bus));

        p_selector_remap->sel_grp_id[i] = (0==GetIpeHashMergeBusSel0(V, g_0_sel_f+packet_type[0]*step1, &merge_bus)? 
            i : GetIpeHashMergeBusSel0(V, g_0_sel_f+packet_type[0]*step1, &merge_bus) - 1);
    }
    
    return CTC_E_NONE;  
}

int32
sys_at_hash_show_selector_remap(uint8 lchip)
{
    uint8 packet_type = 0;
    uint8 sub_type = 0;
    uint8 i = 0;
    uint8 no = 0;
    uint8 sub_type_num[] = {1, SYS_HASH_IP_SUB_TYPE_NUM, SYS_HASH_IP_SUB_TYPE_NUM, SYS_HASH_MPLS_SUB_TYPE_NUM,
                            SYS_HASH_VXLAN_SUB_TYPE_NUM, SYS_HASH_GRE_SUB_TYPE_NUM, 1, 1};
    char* hash_sel_str[] = {"L2", "IPv4", "IPv6", "MPLS", "VxLAN", "GRE", "TRILL", "FCOE"};
    int32 ret = 0;
    ctc_lb_hash_selector_remap_t selector_remap;
    SYS_LB_HASH_INIT_CHECK();
    sal_memset(&selector_remap, 0, sizeof(ctc_lb_hash_selector_remap_t));
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "\n%-4s%-12s%-9s%-30s\n","No.", "packet-type", "sub-type", "selector-order");
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "%-30s\n", "================================================");

    for (packet_type = CTC_LB_HASH_PKT_TYPE_L2; packet_type <= CTC_LB_HASH_PKT_TYPE_FCOE; packet_type++)
    {
        for (sub_type = 0; sub_type < sub_type_num[packet_type-CTC_LB_HASH_PKT_TYPE_L2]; sub_type++)
        {
            selector_remap.packet_type = packet_type;
            selector_remap.sub_type = sub_type;
            ret = sys_at_hash_get_selector_remap(lchip, &selector_remap);
            if (ret < 0)
            {
                continue;
            }
            
            SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "%-4d%-12s%-9d", no++, hash_sel_str[packet_type-CTC_LB_HASH_PKT_TYPE_L2], sub_type);
            for (i = 0; i < CTC_LB_HASH_GRP_NUM; i++)
            {
                SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "%-3d", selector_remap.sel_grp_id[i]);
            }
            SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "\n");
        }
    }
    
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_DUMP, "%-30s\n", "================================================");
    
    return CTC_E_NONE;
}

#define _____________________________PARSER_LB_HASH___________________________________

int32
_sys_at_hash_set_mask_profile_en(uint8 lchip, uint8 sel_grp_id, uint8 enable)
{
    IpeHashCtl0_m hash_ctl;
    uint8 step1 = 0;
    uint8 step2 = 0;
    uint8 i = 0;

    SYS_LB_HASH_COMPATIBLE_IOR(lchip, sel_grp_id, hash_ctl);

    if (enable == GetIpeHashCtl0(V, u_g2_0_overwriteBulkMaskValid_f, &hash_ctl))
    {
        return CTC_E_NONE;
    }

    /*change mode need clear all bitmap*/
    step1 = IpeHashCtl0_u_g2_1_overwriteBulkMaskValid_f - IpeHashCtl0_u_g2_0_overwriteBulkMaskValid_f;
    step2 = IpeHashCtl0_u_g2_1_bulkBitmap_f - IpeHashCtl0_u_g2_0_bulkBitmap_f;
    for (i = 0; i < SYS_HASH_MASK_PROFILE_NUM; i++)
    {
        SetIpeHashCtl0(V, u_g2_0_overwriteBulkMaskValid_f+i*step1, &hash_ctl, enable);
        SetIpeHashCtl0(V, u_g2_0_bulkBitmap_f+i*step2, &hash_ctl, 0);
    }

    SYS_LB_HASH_COMPATIBLE_IOW(lchip, sel_grp_id, hash_ctl);

    return CTC_E_NONE;
}

int32
sys_at_hash_set_udf_hash_en(uint8 lchip, void* hash_poly)
{
    ctc_lb_hash_poly_t* p_hash_poly = (ctc_lb_hash_poly_t*)hash_poly;
    IpeHashCtl0_m hash_ctl;
    uint32 value = 0;
    uint16 mask_low = p_hash_poly->seed & 0xFFFF;
    uint16 mask_high = (p_hash_poly->seed >> 16) & 0xFFFF;

    SYS_LB_HASH_COMPATIBLE_IOR(lchip, p_hash_poly->sel_grp_id, hash_ctl);

    /* set udf offset */
    value = (0 == (p_hash_poly->udf_offset >> 5)) ? CTC_LB_HASH_UDF_SEL_MODE0
          : (1 == (p_hash_poly->udf_offset >> 5)) ? CTC_LB_HASH_UDF_SEL_MODE1
          : (2 == (p_hash_poly->udf_offset >> 5)) ? CTC_LB_HASH_UDF_SEL_MODE2
          : CTC_LB_HASH_UDF_SEL_MODE3;
    SetIpeHashCtl0(V, udfDataSel_f, &hash_ctl, value);

    /* enable do not calculate */
    SetIpeHashCtl0(V, hashPolySelectA_f, &hash_ctl, SYS_LB_HASHPOLY_NO_CALC);

    /* select bulk pos */
    value = (mask_low != 0) ? SYS_HASH_SEED1_BULK_POS : SYS_HASH_SEED2_BULK_POS;
    SetIpeHashCtl0(V, hashPolySelectB_f, &hash_ctl, value);

    /* set 16 bit mask */
    SetIpeHashCtl0(V, hashPolySelectBMask_f, &hash_ctl, (mask_low != 0) ? mask_low : mask_high);

    SYS_LB_HASH_COMPATIBLE_IOW(lchip, p_hash_poly->sel_grp_id, hash_ctl);

    return CTC_E_NONE;
}

int32
sys_at_hash_get_udf_hash_en(uint8 lchip, void* hash_poly)
{
    ctc_lb_hash_poly_t* p_hash_poly = (ctc_lb_hash_poly_t*)hash_poly;
    IpeHashCtl0_m hash_ctl;
    uint32 value = 0;

    SYS_LB_HASH_COMPATIBLE_IOR(lchip, p_hash_poly->sel_grp_id, hash_ctl);

    /* get udf offset */
    value = GetIpeHashCtl0(V, udfDataSel_f, &hash_ctl);
    p_hash_poly->udf_offset = CTC_LB_HASH_UDF_SEL_MODE0 == value ? 0
                            : CTC_LB_HASH_UDF_SEL_MODE1 == value ? 32
                            : CTC_LB_HASH_UDF_SEL_MODE2 == value ? 64 : 96;

    /* get bulk pos */
    value = GetIpeHashCtl0(V, hashPolySelectB_f, &hash_ctl);
    if (SYS_HASH_SEED2_BULK_POS == value)
    {
        p_hash_poly->udf_offset += 16;
    }

    /* get 16bit mask */
    p_hash_poly->seed = GetIpeHashCtl0(V, hashPolySelectBMask_f, &hash_ctl);

    return CTC_E_NONE;
}

/* ------------ set packet field -------------- */

STATIC int32
_sys_at_hash_set_l2_template_en(uint8 lchip, uint8 sel_id, uint8 packet_type, uint8 disable)
{
    uint8 tunnel_ctl = 0;
    IpeHashCtl0_m hash_ctl;

    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "sel_id = %d, packet_type = %d, disable = %d\n", sel_id, packet_type, disable);

    SYS_LB_HASH_COMPATIBLE_IOR(lchip, sel_id, hash_ctl);

    switch (packet_type)
    {
    case SYS_HASH_FWD_TYPE_IPV4:
        SetIpeHashCtl(V, disableIpv4Hash_f, &hash_ctl, disable);
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 0): CTC_BIT_UNSET(tunnel_ctl, 0);
        SetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV6:
        SetIpeHashCtl(V, disableIpv6Hash_f, &hash_ctl, disable);
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 0): CTC_BIT_UNSET(tunnel_ctl, 0);
        SetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_MPLS:
        SetIpeHashCtl(V, disableMplsHash_f, &hash_ctl, disable);
        break;
    case SYS_HASH_FWD_TYPE_TRILL_DECAP:
        SetIpeHashCtl(V, disableTrillHash_f, &hash_ctl, disable);
        break;
    case SYS_HASH_FWD_TYPE_FCOE:
        SetIpeHashCtl(V, disableFcoeHash_f, &hash_ctl, disable);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_INNER_IPV4:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 2): CTC_BIT_UNSET(tunnel_ctl, 2);
        SetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_INNER_IPV6:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 1): CTC_BIT_UNSET(tunnel_ctl, 1);
        SetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_INNER_IPV4:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 2): CTC_BIT_UNSET(tunnel_ctl, 2);
        SetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_INNER_IPV6:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 1): CTC_BIT_UNSET(tunnel_ctl, 1);
        SetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV4:
    case SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV6:
        tunnel_ctl = GetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 1): CTC_BIT_UNSET(tunnel_ctl, 1);
        SetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_MPLS_VPLS:
        tunnel_ctl = GetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 0): CTC_BIT_UNSET(tunnel_ctl, 0);
        SetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_MPLS_VPWS:
        tunnel_ctl = GetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 2): CTC_BIT_UNSET(tunnel_ctl, 2);
        SetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_VXLAN:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 5): CTC_BIT_UNSET(tunnel_ctl, 5);
        SetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_VXLAN:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 5): CTC_BIT_UNSET(tunnel_ctl, 5);
        SetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_GRE:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 3): CTC_BIT_UNSET(tunnel_ctl, 3);
        SetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_GRE:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 3): CTC_BIT_UNSET(tunnel_ctl, 3);
        SetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_NVGRE:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 4): CTC_BIT_UNSET(tunnel_ctl, 4);
        SetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl, tunnel_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_NVGRE:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        disable? CTC_BIT_SET(tunnel_ctl, 4): CTC_BIT_UNSET(tunnel_ctl, 4);
        SetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl, tunnel_ctl);
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }
    SYS_LB_HASH_COMPATIBLE_IOW(lchip, sel_id, hash_ctl);

    return CTC_E_NONE;
}

STATIC uint32
_sys_at_hash_get_field_info(uint32 fields, sys_hash_field_info_t *field_info)
{
    field_info->vni_en = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_VNI_LO) || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_VNI_HI);
    field_info->flow_label_en = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_FLOWLABEL_LO) || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_FLOWLABEL_HI);
    field_info->udf_field_en = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_UDF_LO) || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_UDF_HI);
    field_info->l4_port_en = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_L4_DSTPORT) || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_L4_SRCPORT);
    field_info->label_en = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_LABEL0_LO)
                        || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_LABEL1_LO)
                        || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_LABEL2_LO);
    field_info->mac_en = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_MACDA_LO) || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_MACDA_MI)
                      || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_MACDA_HI) || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_MACSA_LO)
                      || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_MACSA_MI) || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_MACSA_HI);
    field_info->vlan_en = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_SVLAN) || CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_CVLAN);
    field_info->l4_protocol_en = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_PROTOCOL);
    field_info->is_inner = CTC_FLAG_ISSET(fields, CTC_LB_HASH_FIELD_INNER);
    field_info->only_l2 = SYS_LB_HASH_IS_ONLY_L2_FIELD(fields);

    return CTC_E_NONE;
}

STATIC uint32
_sys_at_hash_set_template_mask(uint8 lchip, uint8 field_type, sys_hash_cfg_data_t* p_cfg_data, uint8 flag)
{
    sys_hash_field_info_t field_info;
    uint32 bulk_mask = 0;
    uint32 fields = p_cfg_data->fields;
    uint8 mode = 0;
    uint8 label_en = 0;
    uint8 mac_en = 0;
    uint8 flow_label_en = 0;
    uint8 vni_en = 0;

    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    sal_memset(&field_info, 0, sizeof(sys_hash_field_info_t));
    CTC_ERROR_RETURN(_sys_at_hash_get_field_info(fields, &field_info));
    label_en = field_info.label_en && !field_info.is_inner;
    mac_en = field_info.mac_en && field_info.is_inner;
    flow_label_en = field_info.flow_label_en;
    vni_en = field_info.vni_en;

    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "sel_id = %d, field_type = %d, vni_en = %d, flow_label_en = %d, udf_field_en = %d, label_en = %d, mac_en = %d\n",
                        p_cfg_data->sel_id, field_type, vni_en, flow_label_en, field_info.udf_field_en, label_en, mac_en);

    switch (field_type)
    {
    case SYS_HASH_FIELD_TYPE_L2:
        mode = vni_en ? 1 : 0;
        HASH_KEY_TEMPLATE0(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_IP:
        mode = vni_en ? 1 : flow_label_en ? 2 : 0;
        HASH_KEY_TEMPLATE4(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_MPLS:
        mode = label_en ? 0 : mac_en ? 1 : 2;
        HASH_KEY_TEMPLATE3(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_VXLAN:
        mode = vni_en ? 1 : 2;
        HASH_KEY_TEMPLATE4(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_GRE:
        mode = flow_label_en ? 2 : 0;
        HASH_KEY_TEMPLATE5(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_NVGRE:
        mode = flow_label_en ? 3 : 1;
        HASH_KEY_TEMPLATE5(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_TRILL:
        HASH_KEY_TEMPLATE1(bulk_mask, fields);
        break;
    case SYS_HASH_FIELD_TYPE_FCOE:
        HASH_KEY_TEMPLATE2(bulk_mask, fields);
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    return bulk_mask;
}

STATIC int32
_sys_at_hash_set_packet_field_asic(uint8 lchip, sys_hash_cfg_data_t* p_cfg_data)
{
    IpeHashCtl_m hash_ctl;
    IpePreLookupHashMap_m pre_hash_map;
    ctc_lb_hash_config_t lb_hash_cfg;
    uint32 cmd = 0;
    uint8 profile_id = 0;
    uint8 step1 = IpePreLookupHashMap_g_1_hashMaskProfileId_f - IpePreLookupHashMap_g_0_hashMaskProfileId_f;
    uint8 step2 = IpeHashCtl0_u_g2_1_bulkBitmap_f - IpeHashCtl0_u_g2_0_bulkBitmap_f;
    uint8 i = 0;
    
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    /* enable profile */
    CTC_ERROR_RETURN(_sys_at_hash_set_mask_profile_en(lchip, p_cfg_data->sel_id, 1));

    /* set field */
    SYS_LB_HASH_COMPATIBLE_IOR(lchip, p_cfg_data->sel_id, hash_ctl);
    
    for (i = 0; i < p_cfg_data->type_cnt; i++)
    {
        uint16 bulk_mask = 0;
        bulk_mask = _sys_at_hash_set_template_mask(lchip, p_cfg_data->field_type[i], p_cfg_data, !i);
        cmd = DRV_IOR(IpePreLookupHashMap_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, p_cfg_data->profile_type[i]/8, cmd, &pre_hash_map));
        profile_id = GetIpePreLookupHashMap(V,g_0_hashMaskProfileId_f+p_cfg_data->profile_type[i]%8*step1,&pre_hash_map);
        SetIpeHashCtl0(V, u_g2_0_bulkBitmap_f+profile_id*step2, &hash_ctl, bulk_mask);
    }

    SYS_LB_HASH_COMPATIBLE_IOW(lchip, p_cfg_data->sel_id, hash_ctl);

    /* set control */
    sal_memset(&lb_hash_cfg, 0, sizeof(ctc_lb_hash_config_t));
    lb_hash_cfg.sel_id = p_cfg_data->sel_id;
    lb_hash_cfg.cfg_type = CTC_LB_HASH_CFG_HASH_CONTROL;
    for (i=0;i<p_cfg_data->ctrl_cnt;i++)
    {
        lb_hash_cfg.value = p_cfg_data->ctrl_value[i];
        lb_hash_cfg.hash_control = p_cfg_data->ctrl_type[i];
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_set_hash_control(lchip, &lb_hash_cfg));
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_set_ipv4_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    uint8 is_inner = CTC_FLAG_ISSET(p_hash_fields->fields0, CTC_LB_HASH_FIELD_INNER);
    uint8 is_l2 = SYS_LB_HASH_IS_ONLY_L2_FIELD(p_hash_fields->fields0);

    switch(p_hash_fields->sub_type)
    {
    case 0: /* not sub_type1 && not sub_type2 */
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_BRIDGE_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_UDP_TUNNEL;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    case 1: /* IPv4 in IPv4 */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_IPV4_IN4;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_INNER_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    case 2: /* IPv6 in IPv4 */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_IPV6_IN4;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_INNER_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }
    
    CTC_ERROR_RETURN(_sys_at_hash_set_l2_template_en(lchip, p_hash_fields->sel_grp_id, p_cfg_data->profile_type[0], (!is_inner && is_l2) ? 1 : 0));
    CTC_ERROR_RETURN(_sys_at_hash_set_packet_field_asic(lchip, p_cfg_data));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_set_ipv6_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    uint8 is_inner = CTC_FLAG_ISSET(p_hash_fields->fields0, CTC_LB_HASH_FIELD_INNER);
    uint8 is_l2 = SYS_LB_HASH_IS_ONLY_L2_FIELD(p_hash_fields->fields0);

    switch(p_hash_fields->sub_type)
    {
    case 0:
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_BRIDGE_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_UDP_TUNNEL;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    case 1: /* IPv4 in IPv6 */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_IPV4_IN6;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_INNER_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    case 2: /* IPv6 in IPv6 */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_IPV6_IN6;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_INNER_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }
    
    CTC_ERROR_RETURN(_sys_at_hash_set_l2_template_en(lchip, p_hash_fields->sel_grp_id, p_cfg_data->profile_type[0], (!is_inner && is_l2) ? 1 : 0));
    CTC_ERROR_RETURN(_sys_at_hash_set_packet_field_asic(lchip, p_cfg_data));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_set_mpls_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    sys_hash_field_info_t field_info;
    uint8 is_inner = 0;
    uint8 is_l2 = 0;

    sal_memset(&field_info, 0, sizeof(sys_hash_field_info_t));
    CTC_ERROR_RETURN(_sys_at_hash_get_field_info(p_hash_fields->fields0, &field_info));
    is_inner = field_info.is_inner;
    is_l2 = field_info.only_l2;

    switch (p_hash_fields->sub_type)
    {
    case 0:
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_INNER_OTHER;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_INNER_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_INNER_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (!is_inner && is_l2) ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS;
        break;
    case 1: /* L3VPN inner is IPv4 */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = SYS_LB_HASH_CONTROL_L3VPN_USE_OUTER_L3;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_inner ? SYS_HASH_FIELD_TYPE_IP : (is_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS);
        break;
    case 2: /* L3VPN inner is IPv6 */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = SYS_LB_HASH_CONTROL_L3VPN_USE_OUTER_L3;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_inner ? SYS_HASH_FIELD_TYPE_IP : (is_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS);
        break;
    case 3: /* VPLS */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = SYS_LB_HASH_CONTROL_L2VPN_USE_OUTER_L3;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_L2VPN_USE_INNER_L2;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = (is_inner && is_l2) ? 1 : 0;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_VPLS;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_MPLS);
        break;
    case 4: /* VPWS */
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_VPWS;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS;
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    if ((field_info.label_en && !is_inner) 
    || ((field_info.mac_en || field_info.l4_port_en || field_info.l4_protocol_en) && is_inner))
    {
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = SYS_LB_HASH_CONTROL_MPLS_TRANSMIT_MODE;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = (field_info.label_en && !is_inner) ? 1 : (field_info.mac_en && is_inner) ? 0 : 2;
    }

    CTC_ERROR_RETURN(_sys_at_hash_set_l2_template_en(lchip, p_hash_fields->sel_grp_id, p_cfg_data->profile_type[0], (!is_inner && is_l2) ? 1 : 0));
    CTC_ERROR_RETURN(_sys_at_hash_set_packet_field_asic(lchip, p_cfg_data));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_set_vxlan_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    sys_hash_field_info_t field_info;
    uint8 is_inner = 0;
    uint8 is_l2 = 0;

    sal_memset(&field_info, 0, sizeof(sys_hash_field_info_t));
    CTC_ERROR_RETURN(_sys_at_hash_get_field_info(p_hash_fields->fields0, &field_info));
    is_inner = field_info.is_inner;
    is_l2 = field_info.only_l2;

    switch (p_hash_fields->sub_type)
    {
    case 0:
        return CTC_E_NONE;
    case 1: /* IPv4 VxLan */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_VXLAN_IN4;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_VXLAN_USE_INNER_L2;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = (is_inner && is_l2) ? 1 : 0;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_VXLAN;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_VXLAN;
        break;
    case 2: /* IPv6 VxLan */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_VXLAN_IN6;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_VXLAN_USE_INNER_L2;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = (is_inner && is_l2) ? 1 : 0;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_VXLAN;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_VXLAN;
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    if (is_inner && is_l2 && (field_info.vni_en || field_info.vlan_en))
    {
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = SYS_LB_HASH_CONTROL_VXLAN_INNER_L2_USE_VNI;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = field_info.vni_en ? 1 : 0;
    }

    CTC_ERROR_RETURN(_sys_at_hash_set_l2_template_en(lchip, p_hash_fields->sel_grp_id, p_cfg_data->profile_type[0], (!is_inner && is_l2) ? 1 : 0));
    CTC_ERROR_RETURN(_sys_at_hash_set_packet_field_asic(lchip, p_cfg_data));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_set_gre_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    sys_hash_field_info_t field_info;
    uint8 is_inner = 0;
    uint8 is_l2 = 0;

    sal_memset(&field_info, 0, sizeof(sys_hash_field_info_t));
    CTC_ERROR_RETURN(_sys_at_hash_get_field_info(p_hash_fields->fields0, &field_info));
    is_inner = field_info.is_inner;
    is_l2 = field_info.only_l2;

    switch (p_hash_fields->sub_type)
    {
    case 0:
        return CTC_E_NONE;
    case 1: /* IPv4 Gre */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_GRE_IN4;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_GRE;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_inner ? SYS_HASH_FIELD_TYPE_IP : (is_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_GRE);
        break;
    case 2: /* IPv6 Gre */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_GRE_IN6;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_GRE;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_inner ? SYS_HASH_FIELD_TYPE_IP : (is_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_GRE);
        break;
    case 3: /* IPv4 NvGre */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_NVGRE_AWARE;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = 1;
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_NVGRE_IN4;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_NVGRE_USE_INNER_L2;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = (is_inner && is_l2) ? 1 : 0;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_NVGRE;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_NVGRE);
        break;
    case 4: /* IPv6 NvGre */
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_NVGRE_AWARE;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = 1;
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_NVGRE_IN6;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = is_inner ? 0 : 1;
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_NVGRE_USE_INNER_L2;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = (is_inner && is_l2) ? 1 : 0;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_NVGRE;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_NVGRE);
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    if (is_inner && is_l2 && (field_info.vni_en || field_info.vlan_en))
    {
        p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = SYS_LB_HASH_CONTROL_GRE_INNER_L2_USE_VSID;
        p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = field_info.vni_en ? 1 : 0;
    }

    CTC_ERROR_RETURN(_sys_at_hash_set_l2_template_en(lchip, p_hash_fields->sel_grp_id, p_cfg_data->profile_type[0], (!is_inner && is_l2) ? 1 : 0));
    CTC_ERROR_RETURN(_sys_at_hash_set_packet_field_asic(lchip, p_cfg_data));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_set_trill_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    uint8 is_inner = CTC_FLAG_ISSET(p_hash_fields->fields0, CTC_LB_HASH_FIELD_INNER);
    uint8 is_l2 = SYS_LB_HASH_IS_ONLY_L2_FIELD(p_hash_fields->fields0);

    p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_TRILL_DECAP_USE_INNER_L2;
    p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = (is_inner && is_l2) ? 1 : 0;
    p_cfg_data->ctrl_type[p_cfg_data->ctrl_cnt] = CTC_LB_HASH_CONTROL_TRILL_TRANSMIT_MODE;
    p_cfg_data->ctrl_value[p_cfg_data->ctrl_cnt++] = !is_inner ? 3 : (is_l2 ? 1 : 2);

    p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_TRILL_DECAP;
    p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_TRILL);
    p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_TRILL_TRANSIT;
    p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_TRILL);

    CTC_ERROR_RETURN(_sys_at_hash_set_l2_template_en(lchip, p_hash_fields->sel_grp_id, p_cfg_data->profile_type[0], (!is_inner && is_l2) ? 1 : 0));
    CTC_ERROR_RETURN(_sys_at_hash_set_packet_field_asic(lchip, p_cfg_data));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_set_packet_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields)
{
    sys_hash_cfg_data_t cfg_data;
    ctc_lb_hash_fields_t hash_fields;
    uint8 is_inner = 0;
    uint8 is_l2 = 0;
    uint8 sub_type_end = 0;

    sal_memcpy(&hash_fields, p_hash_fields, sizeof(ctc_lb_hash_fields_t));
    is_inner = CTC_FLAG_ISSET(hash_fields.fields0, CTC_LB_HASH_FIELD_INNER);
    is_l2 = SYS_LB_HASH_IS_ONLY_L2_FIELD(hash_fields.fields0);

    switch(hash_fields.packet_type)
    {
    case CTC_LB_HASH_PKT_TYPE_L2:
        sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
        cfg_data.sel_id = hash_fields.sel_grp_id;
        cfg_data.fields = hash_fields.fields0;
        cfg_data.profile_type[cfg_data.type_cnt] = SYS_HASH_FWD_TYPE_BRIDGE;
        cfg_data.field_type[cfg_data.type_cnt++] = SYS_HASH_FIELD_TYPE_L2;
        cfg_data.profile_type[cfg_data.type_cnt] = SYS_HASH_FWD_TYPE_FROM_CFLEX;
        cfg_data.field_type[cfg_data.type_cnt++] = SYS_HASH_FIELD_TYPE_L2;
        CTC_ERROR_RETURN(_sys_at_hash_set_packet_field_asic(lchip, &cfg_data));
        break;

    case CTC_LB_HASH_PKT_TYPE_IPV4:
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_IP_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            cfg_data.fields = hash_fields.fields0;
            CTC_ERROR_RETURN(_sys_at_hash_set_ipv4_field(lchip, &hash_fields, &cfg_data));
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_IPV6:
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_IP_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            cfg_data.fields = hash_fields.fields0;
            CTC_ERROR_RETURN(_sys_at_hash_set_ipv6_field(lchip, &hash_fields, &cfg_data));
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_MPLS: 
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_MPLS_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            cfg_data.fields = hash_fields.fields0;
            CTC_ERROR_RETURN(_sys_at_hash_set_mpls_field(lchip, &hash_fields, &cfg_data));
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_VXLAN:
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_VXLAN_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            cfg_data.fields = hash_fields.fields0;
            CTC_ERROR_RETURN(_sys_at_hash_set_vxlan_field(lchip, &hash_fields, &cfg_data));
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_GRE:
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_GRE_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            cfg_data.fields = hash_fields.fields0;
            CTC_ERROR_RETURN(_sys_at_hash_set_gre_field(lchip, &hash_fields, &cfg_data));
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_TRILL:
        sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
        cfg_data.sel_id = hash_fields.sel_grp_id;
        cfg_data.fields = hash_fields.fields0;
        CTC_ERROR_RETURN(_sys_at_hash_set_trill_field(lchip, &hash_fields, &cfg_data));
        break;

    case CTC_LB_HASH_PKT_TYPE_FCOE:
        sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
        cfg_data.sel_id = hash_fields.sel_grp_id;
        cfg_data.fields = hash_fields.fields0;
        cfg_data.profile_type[cfg_data.type_cnt] = SYS_HASH_FWD_TYPE_FCOE;
        cfg_data.field_type[cfg_data.type_cnt++] = is_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_FCOE;
        CTC_ERROR_RETURN(_sys_at_hash_set_l2_template_en(lchip, hash_fields.sel_grp_id, cfg_data.profile_type[0], (!is_inner && is_l2) ? 1 : 0));
        CTC_ERROR_RETURN(_sys_at_hash_set_packet_field_asic(lchip, &cfg_data));
        break;

    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_set_selector_merge(uint8 lchip, ctc_lb_hash_fields_t* hash_fields)
{
    ctc_lb_hash_selector_ext_t lb_hash_selector_ext;
    ctc_lb_hash_selector_remap_t lb_hash_selector_remap;
    uint8 enable = (0 != hash_fields->fields1);
    uint8 sel_grp_id = hash_fields->sel_grp_id;

    sal_memset(&lb_hash_selector_ext, 0, sizeof(ctc_lb_hash_selector_ext_t));
    sal_memset(&lb_hash_selector_remap, 0, sizeof(ctc_lb_hash_selector_remap_t));

    if (enable)
    {
        /* 1. set selector merge */
        lb_hash_selector_ext.sel_grp_id = MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) + sel_grp_id / 2;
        CTC_BIT_SET(lb_hash_selector_ext.sel_grp_bitmap, sel_grp_id);
        CTC_BIT_SET(lb_hash_selector_ext.sel_grp_bitmap, sel_grp_id + 1);
        CTC_ERROR_RETURN(sys_at_hash_set_selector_ext(lchip, &lb_hash_selector_ext));

        /* 2. get selector mapping */
        lb_hash_selector_remap.packet_type = hash_fields->packet_type;
        lb_hash_selector_remap.sub_type = hash_fields->sub_type;
        CTC_ERROR_RETURN(sys_at_hash_get_selector_remap(lchip, &lb_hash_selector_remap));

        /* 3. set selector mapping */
        lb_hash_selector_remap.sel_grp_id[sel_grp_id] = MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) + sel_grp_id / 2;
        CTC_ERROR_RETURN(sys_at_hash_set_selector_remap(lchip, &lb_hash_selector_remap));
    }
    else
    {
        /* 1. reset selector merge */
        lb_hash_selector_ext.sel_grp_id = MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) + sel_grp_id / 2;
        lb_hash_selector_ext.sel_grp_bitmap = 0;
        CTC_ERROR_RETURN(sys_at_hash_set_selector_ext(lchip, &lb_hash_selector_ext));

        /* 2. get selector mapping */
        lb_hash_selector_remap.packet_type = hash_fields->packet_type;
        lb_hash_selector_remap.sub_type = hash_fields->sub_type;
        CTC_ERROR_RETURN(sys_at_hash_get_selector_remap(lchip, &lb_hash_selector_remap));

        /* 3. reset selector mapping */
        lb_hash_selector_remap.sel_grp_id[sel_grp_id] = sel_grp_id;
        CTC_ERROR_RETURN(sys_at_hash_set_selector_remap(lchip, &lb_hash_selector_remap));
    }

    return CTC_E_NONE;
}

int32
sys_at_hash_set_field(uint8 lchip, void* p_hash_fields)
{
    ctc_lb_hash_fields_t* p_lb_hash_fields = (ctc_lb_hash_fields_t*)p_hash_fields;
    ctc_lb_hash_fields_t lb_hash_fields_tmp;
    ctc_lb_hash_selector_ext_t lb_hash_selector_ext;
    uint8 merge_en = 0;
    uint8 sel_grp_id = p_lb_hash_fields->sel_grp_id;

    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_LB_HASH_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "sel_id = %d, packet_type = %d, sub_type = %d, fields0 = 0x%x, fields1 = 0x%x\n",
                        p_lb_hash_fields->sel_grp_id, p_lb_hash_fields->packet_type, p_lb_hash_fields->sub_type, 
                        p_lb_hash_fields->fields0, p_lb_hash_fields->fields1);

    /* 4 selector mode do not support merge */
    if ((0 != p_lb_hash_fields->fields1) && SYS_LB_HASH_IS_COMPATIBLE_MODE)
    {
        return CTC_E_NOT_SUPPORT;
    }

    /* odd selector do not support fields1 */
    if ((0 != p_lb_hash_fields->fields1) && (0 != sel_grp_id % 2))
    {
        return CTC_E_INVALID_CONFIG;
    }
    
    /* do not support set second selector when enable merge */
    if((MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) != SYS_USW_LB_HASH_CTL_NUM))
    {
        sal_memset(&lb_hash_selector_ext, 0, sizeof(ctc_lb_hash_selector_ext_t));
        lb_hash_selector_ext.sel_grp_id = MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) + sel_grp_id / 2;
        CTC_ERROR_RETURN(sys_at_hash_get_selector_ext(lchip, &lb_hash_selector_ext));
        merge_en = CTC_IS_BIT_SET(lb_hash_selector_ext.sel_grp_bitmap, sel_grp_id);
    }

    /* set hash fields0 */
    CTC_ERROR_RETURN(_sys_at_hash_set_packet_field(lchip, p_lb_hash_fields));

    sal_memset(&lb_hash_fields_tmp, 0, sizeof(ctc_lb_hash_fields_t));
    if (0 != p_lb_hash_fields->fields1)
    {
        /* set hash fields1 */
        lb_hash_fields_tmp.sel_grp_id = sel_grp_id + 1;
        lb_hash_fields_tmp.packet_type = p_lb_hash_fields->packet_type;
        lb_hash_fields_tmp.sub_type = p_lb_hash_fields->sub_type;
        lb_hash_fields_tmp.fields0 = p_lb_hash_fields->fields1;
        CTC_ERROR_RETURN(_sys_at_hash_set_packet_field(lchip, &lb_hash_fields_tmp));
       
        /* enable merge */
        CTC_ERROR_RETURN(_sys_at_hash_set_selector_merge(lchip, p_lb_hash_fields));
    }
    else if (merge_en && (0 == sel_grp_id % 2))
    {
        /* reset hash fields1 */
        lb_hash_fields_tmp.sel_grp_id = sel_grp_id + 1;
        lb_hash_fields_tmp.packet_type = p_lb_hash_fields->packet_type;
        lb_hash_fields_tmp.sub_type = p_lb_hash_fields->sub_type;
        lb_hash_fields_tmp.fields0 = p_lb_hash_fields->fields1;
        CTC_ERROR_RETURN(_sys_at_hash_set_packet_field(lchip, &lb_hash_fields_tmp));

        /* disable merge */
        CTC_ERROR_RETURN(_sys_at_hash_set_selector_merge(lchip, p_lb_hash_fields));
    }

    return CTC_E_NONE;
}

/* ----------------- get packet field ----------------- */

STATIC int32
_sys_at_hash_get_l2_template_en(uint8 lchip, uint8 sel_id, uint8 packet_type, uint8* disable)
{
    uint8 tunnel_ctl = 0;
    IpeHashCtl0_m hash_ctl;

    SYS_LB_HASH_COMPATIBLE_IOR(lchip, sel_id, hash_ctl);

    switch (packet_type)
    {
    case SYS_HASH_FWD_TYPE_IPV4:
        *disable = GetIpeHashCtl(V, disableIpv4Hash_f, &hash_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV6:
        *disable = GetIpeHashCtl(V, disableIpv6Hash_f, &hash_ctl);
        break;
    case SYS_HASH_FWD_TYPE_MPLS:
        *disable = GetIpeHashCtl(V, disableMplsHash_f, &hash_ctl);
        break;
    case SYS_HASH_FWD_TYPE_TRILL_DECAP:
        *disable = GetIpeHashCtl(V, disableTrillHash_f, &hash_ctl);
        break;
    case SYS_HASH_FWD_TYPE_FCOE:
        *disable = GetIpeHashCtl(V, disableFcoeHash_f, &hash_ctl);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_INNER_IPV4:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 2);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_INNER_IPV6:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 1);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_INNER_IPV4:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 2);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_INNER_IPV6:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 1);
        break;
    case SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV4:
    case SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV6:
        tunnel_ctl = GetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 1);
        break;
    case SYS_HASH_FWD_TYPE_MPLS_VPLS:
        tunnel_ctl = GetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 0);
        break;
    case SYS_HASH_FWD_TYPE_MPLS_VPWS:
        tunnel_ctl = GetIpeHashCtl0(V, mplsTunnelDisableMplsHash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 2);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_VXLAN:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 5);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_VXLAN:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 5);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_GRE:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 3);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_GRE:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 3);
        break;
    case SYS_HASH_FWD_TYPE_IPV4_NVGRE:
        tunnel_ctl = GetIpeHashCtl0(V, v4TunnelDisableIpv4Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 4);
        break;
    case SYS_HASH_FWD_TYPE_IPV6_NVGRE:
        tunnel_ctl = GetIpeHashCtl0(V, v6TunnelDisableIpv6Hash_f, &hash_ctl);
        *disable = CTC_IS_BIT_SET(tunnel_ctl, 4);
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    return CTC_E_NONE;
}

STATIC uint32
_sys_at_hash_get_template_mask(uint8 lchip, uint32 bulk_mask, sys_hash_cfg_data_t* p_cfg_data, uint32* p_fields)
{
    ctc_lb_hash_config_t lb_hash_cfg;
    uint32 fields = 0;
    uint8 mode = 0;
    uint8 flow_label_en = 0;
    uint8 label_en = 0;
    uint8 mac_en = 0;
    uint8 is_vxlan= (SYS_HASH_FWD_TYPE_IPV4_VXLAN == p_cfg_data->profile_type[0]) || (SYS_HASH_FWD_TYPE_IPV6_VXLAN == p_cfg_data->profile_type[0]);
    uint8 is_nvgre = (SYS_HASH_FWD_TYPE_IPV4_NVGRE == p_cfg_data->profile_type[0]) || (SYS_HASH_FWD_TYPE_IPV6_NVGRE == p_cfg_data->profile_type[0]);
    uint8 vni_en = 0;
    uint8 vsid_en = 0;
    uint8 l2_vni_en = 0;
    uint8 l2_vsid_en = 0;
    uint8 ip_vni_en = 0;
    uint8 ip_vsid_en = 0;
    uint8 is_inner = CTC_FLAG_ISSET(*p_fields, CTC_LB_HASH_FIELD_INNER);
    sal_memset(&lb_hash_cfg, 0, sizeof(ctc_lb_hash_config_t));
    lb_hash_cfg.sel_id = p_cfg_data->sel_id;
    lb_hash_cfg.cfg_type = CTC_LB_HASH_CFG_HASH_CONTROL;

    lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_IPV6_USE_FLOW_LABEL;
    CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
    flow_label_en = lb_hash_cfg.value;
    lb_hash_cfg.hash_control = SYS_LB_HASH_CONTROL_MPLS_TRANSMIT_MODE;
    CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
    mac_en = (0 == lb_hash_cfg.value);
    label_en = (1 == lb_hash_cfg.value);

    lb_hash_cfg.hash_control = SYS_LB_HASH_CONTROL_VXLAN_INNER_L2_USE_VNI;
    CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
    vni_en = lb_hash_cfg.value;
    l2_vni_en = is_vxlan && vni_en && is_inner;
    ip_vni_en = is_vxlan;

    lb_hash_cfg.hash_control = SYS_LB_HASH_CONTROL_GRE_INNER_L2_USE_VSID;
    CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
    vsid_en = lb_hash_cfg.value;
    l2_vsid_en = is_nvgre && vsid_en && is_inner;
    ip_vsid_en = is_nvgre;

    switch (p_cfg_data->field_type[0])
    {
    case SYS_HASH_FIELD_TYPE_L2:
        mode = (l2_vni_en || l2_vsid_en)? 1 : 0;
        GET_HASH_KEY_TEMPLATE0(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_IP:
        mode = (ip_vni_en || ip_vsid_en) ? 1 : flow_label_en ? 2 : 0;
        GET_HASH_KEY_TEMPLATE4(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_MPLS:
        mode = label_en ? 0 : mac_en ? 1 : 2;
        GET_HASH_KEY_TEMPLATE3(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_VXLAN:
        GET_HASH_KEY_TEMPLATE4(bulk_mask, fields, 1);
        break;
    case SYS_HASH_FIELD_TYPE_GRE:
        mode = flow_label_en ? 2 : 0;
        GET_HASH_KEY_TEMPLATE5(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_NVGRE:
        mode = flow_label_en ? 3 : 1;
        GET_HASH_KEY_TEMPLATE5(bulk_mask, fields, mode);
        break;
    case SYS_HASH_FIELD_TYPE_TRILL:
        GET_HASH_KEY_TEMPLATE1(bulk_mask, fields);
        break;
    case SYS_HASH_FIELD_TYPE_FCOE:
        GET_HASH_KEY_TEMPLATE2(bulk_mask, fields);
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    *p_fields |= fields;

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_get_packet_field_asic(uint8 lchip, sys_hash_cfg_data_t* p_cfg_data, uint8 is_inner)
{
    IpeHashCtl_m hash_ctl;
    IpePreLookupHashMap_m pre_hash_map;
    uint32 fields = 0;
    uint32 bulk_mask = 0;
    uint32 cmd = 0;
    uint8 profile_id = 0;
    uint8 step1 = IpePreLookupHashMap_g_1_hashMaskProfileId_f - IpePreLookupHashMap_g_0_hashMaskProfileId_f;
    uint8 step2 = IpeHashCtl0_u_g2_1_bulkBitmap_f - IpeHashCtl0_u_g2_0_bulkBitmap_f;
    uint8 i = 0;

    SYS_LB_HASH_COMPATIBLE_IOR(lchip, p_cfg_data->sel_id, hash_ctl);
    
    /* get field */
    for (i = 0; i < p_cfg_data->type_cnt; i++)
    {
        cmd = DRV_IOR(IpePreLookupHashMap_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, p_cfg_data->profile_type[i]/8, cmd, &pre_hash_map));
        profile_id = GetIpePreLookupHashMap(V,g_0_hashMaskProfileId_f+p_cfg_data->profile_type[i]%8*step1,&pre_hash_map);
        bulk_mask = GetIpeHashCtl0(V, u_g2_0_bulkBitmap_f+profile_id*step2, &hash_ctl);
        is_inner ? CTC_SET_FLAG(fields, CTC_LB_HASH_FIELD_INNER) : CTC_UNSET_FLAG(fields, CTC_LB_HASH_FIELD_INNER);
        CTC_ERROR_RETURN(_sys_at_hash_get_template_mask(lchip, bulk_mask, p_cfg_data, &fields));
        p_cfg_data->fields |= fields;
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_get_ipv4_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    ctc_lb_hash_config_t lb_hash_cfg;
    uint8 is_outer_l2 = 0;
    uint8 is_inner = 0;

    sal_memset(&lb_hash_cfg, 0, sizeof(ctc_lb_hash_config_t));
    lb_hash_cfg.sel_id = p_hash_fields->sel_grp_id;
    lb_hash_cfg.cfg_type = CTC_LB_HASH_CFG_HASH_CONTROL;

    switch(p_hash_fields->sub_type)
    {
    case 0: /* not sub_type1 && not sub_type2 */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV4, &is_outer_l2));
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    case 1: /* IPv4 in IPv4 */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV4_INNER_IPV4, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_IPV4_IN4;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = (0 == lb_hash_cfg.value);
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_INNER_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    case 2: /* IPv6 in IPv4 */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV4_INNER_IPV6, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_IPV6_IN4;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = (0 == lb_hash_cfg.value);
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_INNER_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    CTC_ERROR_RETURN(_sys_at_hash_get_packet_field_asic(lchip, p_cfg_data, is_inner));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_get_ipv6_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    ctc_lb_hash_config_t lb_hash_cfg;
    uint8 is_outer_l2 = 0;
    uint8 is_inner = 0;

    sal_memset(&lb_hash_cfg, 0, sizeof(ctc_lb_hash_config_t));
    lb_hash_cfg.sel_id = p_hash_fields->sel_grp_id;
    lb_hash_cfg.cfg_type = CTC_LB_HASH_CFG_HASH_CONTROL;

    switch(p_hash_fields->sub_type)
    {
    case 0: /* not sub_type1 && not sub_type2 */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV6, &is_outer_l2));
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    case 1: /* IPv4 in IPv6 */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV6_INNER_IPV4, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_IPV4_IN6;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = (0 == lb_hash_cfg.value);
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_INNER_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    case 2: /* IPv6 in IPv6 */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV6_INNER_IPV6, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_IPV6_IN6;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = (0 == lb_hash_cfg.value);
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_INNER_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_IP;
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    CTC_ERROR_RETURN(_sys_at_hash_get_packet_field_asic(lchip, p_cfg_data, is_inner));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_get_mpls_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    ctc_lb_hash_config_t lb_hash_cfg;
    uint8 is_outer_l2 = 0;
    uint8 is_outer_l3 = 0;
    uint8 is_inner_l2 = 0;
    uint8 is_inner = 0;

    sal_memset(&lb_hash_cfg, 0, sizeof(ctc_lb_hash_config_t));
    lb_hash_cfg.sel_id = p_hash_fields->sel_grp_id;
    lb_hash_cfg.cfg_type = CTC_LB_HASH_CFG_HASH_CONTROL;

    switch (p_hash_fields->sub_type)
    {
    case 0:
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_MPLS, &is_outer_l2));
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS;
        break;
    case 1: /* L3VPN inner is IPv4 */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV4, &is_outer_l2));
        lb_hash_cfg.hash_control = SYS_LB_HASH_CONTROL_L3VPN_USE_OUTER_L3;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_outer_l3 = lb_hash_cfg.value;
        is_inner = (!is_outer_l2 && !is_outer_l3);
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV4;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_outer_l3 ? SYS_HASH_FIELD_TYPE_MPLS : SYS_HASH_FIELD_TYPE_IP);
        break;
    case 2: /* L3VPN inner is IPv6 */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV6, &is_outer_l2));
        lb_hash_cfg.hash_control = SYS_LB_HASH_CONTROL_L3VPN_USE_OUTER_L3;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_outer_l3 = lb_hash_cfg.value;
        is_inner = (!is_outer_l2 && !is_outer_l3);
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_L3VPN_INNER_IPV6;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_outer_l3 ? SYS_HASH_FIELD_TYPE_MPLS : SYS_HASH_FIELD_TYPE_IP);
        break;
    case 3: /* VPLS */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_MPLS_VPLS, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_L2VPN_USE_INNER_L2;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner_l2 = lb_hash_cfg.value;
        lb_hash_cfg.hash_control = SYS_LB_HASH_CONTROL_L2VPN_USE_OUTER_L3;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_outer_l3 = lb_hash_cfg.value;
        is_inner = (!is_outer_l2 && !is_outer_l3);
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_VPLS;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (is_outer_l2 || is_inner_l2) ? SYS_HASH_FIELD_TYPE_L2 
                                                       : (is_outer_l3 ? SYS_HASH_FIELD_TYPE_MPLS : SYS_HASH_FIELD_TYPE_IP);
        break;
    case 4: /* VPWS */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_MPLS_VPWS, &is_outer_l2));
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_MPLS_VPWS;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_MPLS;
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    CTC_ERROR_RETURN(_sys_at_hash_get_packet_field_asic(lchip, p_cfg_data, is_inner));

    return CTC_E_NONE;
}


STATIC int32
_sys_at_hash_get_vxlan_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    ctc_lb_hash_config_t lb_hash_cfg;
    uint8 is_outer_l2 = 0;
    uint8 is_inner_l2 = 0;
    uint8 is_inner = 0;

    sal_memset(&lb_hash_cfg, 0, sizeof(ctc_lb_hash_config_t));
    lb_hash_cfg.sel_id = p_hash_fields->sel_grp_id;
    lb_hash_cfg.cfg_type = CTC_LB_HASH_CFG_HASH_CONTROL;

    switch (p_hash_fields->sub_type)
    {
    case 0:
        return CTC_E_NONE;
    case 1: /* IPv4 VxLan */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV4_VXLAN, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_VXLAN_IN4;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = !lb_hash_cfg.value;
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_VXLAN_USE_INNER_L2;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner_l2 = lb_hash_cfg.value;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_VXLAN;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (is_outer_l2 || is_inner_l2) ? SYS_HASH_FIELD_TYPE_L2 : is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_VXLAN;
        break;
    case 2: /* IPv6 VxLan */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV6_VXLAN, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_VXLAN_IN6;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = !lb_hash_cfg.value;
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_VXLAN_USE_INNER_L2;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner_l2 = lb_hash_cfg.value;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_VXLAN;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (is_outer_l2 || is_inner_l2) ? SYS_HASH_FIELD_TYPE_L2 : is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_VXLAN;
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    CTC_ERROR_RETURN(_sys_at_hash_get_packet_field_asic(lchip, p_cfg_data, is_inner));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_get_gre_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    ctc_lb_hash_config_t lb_hash_cfg;
    uint8 is_outer_l2 = 0;
    uint8 is_inner_l2 = 0;
    uint8 is_inner = 0;

    sal_memset(&lb_hash_cfg, 0, sizeof(ctc_lb_hash_config_t));
    lb_hash_cfg.sel_id = p_hash_fields->sel_grp_id;
    lb_hash_cfg.cfg_type = CTC_LB_HASH_CFG_HASH_CONTROL;

    switch (p_hash_fields->sub_type)
    {
    case 0:
        return CTC_E_NONE;
    case 1: /* IPv4 Gre */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV4_GRE, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_GRE_IN4;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = !lb_hash_cfg.value;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_GRE;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_GRE);
        break;
    case 2: /* IPv6 Gre */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV6_GRE, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_GRE_IN6;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = !lb_hash_cfg.value;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_GRE;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_GRE);
        break;
    case 3: /* IPv4 NvGre */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV4_NVGRE, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_NVGRE_IN4;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = !lb_hash_cfg.value;
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_NVGRE_USE_INNER_L2;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner_l2 = lb_hash_cfg.value;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV4_NVGRE;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (is_outer_l2 || is_inner_l2) ? SYS_HASH_FIELD_TYPE_L2 
                                                       : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_NVGRE);
        break;
    case 4: /* IPv6 NvGre */
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_IPV6_NVGRE, &is_outer_l2));
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_DISABLE_TUNNEL_NVGRE_IN6;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner = !lb_hash_cfg.value;
        lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_NVGRE_USE_INNER_L2;
        CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
        is_inner_l2 = lb_hash_cfg.value;
        p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_IPV6_NVGRE;
        p_cfg_data->field_type[p_cfg_data->type_cnt++] = (is_outer_l2 || is_inner_l2) ? SYS_HASH_FIELD_TYPE_L2 
                                                       : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_NVGRE);
        break;
    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    CTC_ERROR_RETURN(_sys_at_hash_get_packet_field_asic(lchip, p_cfg_data, is_inner));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_get_trill_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields, sys_hash_cfg_data_t* p_cfg_data)
{
    ctc_lb_hash_config_t lb_hash_cfg;
    uint8 is_outer_l2 = 0;
    uint8 is_inner_l2 = 0;
    uint8 is_inner = 0;

    sal_memset(&lb_hash_cfg, 0, sizeof(ctc_lb_hash_config_t));
    lb_hash_cfg.sel_id = p_hash_fields->sel_grp_id;
    lb_hash_cfg.cfg_type = CTC_LB_HASH_CFG_HASH_CONTROL;

    /* get control */
    CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_TRILL_DECAP, &is_outer_l2));
    lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_TRILL_DECAP_USE_INNER_L2;
    CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
    is_inner_l2 = lb_hash_cfg.value;
    lb_hash_cfg.hash_control = CTC_LB_HASH_CONTROL_TRILL_TRANSMIT_MODE;
    CTC_ERROR_RETURN(_sys_tsingma_hash_config_get_hash_control(lchip, &lb_hash_cfg));
    is_inner = (3 != lb_hash_cfg.value);

    p_cfg_data->profile_type[p_cfg_data->type_cnt] = SYS_HASH_FWD_TYPE_TRILL_DECAP;
    p_cfg_data->field_type[p_cfg_data->type_cnt++] = (is_outer_l2 || is_inner_l2) ? SYS_HASH_FIELD_TYPE_L2 : (is_inner ? SYS_HASH_FIELD_TYPE_IP : SYS_HASH_FIELD_TYPE_TRILL);

    CTC_ERROR_RETURN(_sys_at_hash_get_packet_field_asic(lchip, p_cfg_data, is_inner));

    return CTC_E_NONE;
}

STATIC int32
_sys_at_hash_get_packet_field(uint8 lchip, ctc_lb_hash_fields_t* p_hash_fields)
{
    sys_hash_cfg_data_t cfg_data;
    ctc_lb_hash_fields_t hash_fields;
    uint8 sub_type_end = 0;

    sal_memcpy(&hash_fields, p_hash_fields, sizeof(ctc_lb_hash_fields_t));

    switch(hash_fields.packet_type)
    {
    case CTC_LB_HASH_PKT_TYPE_L2:
        sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
        cfg_data.sel_id = hash_fields.sel_grp_id;
        cfg_data.profile_type[cfg_data.type_cnt] = SYS_HASH_FWD_TYPE_BRIDGE;
        cfg_data.field_type[cfg_data.type_cnt++] = SYS_HASH_FIELD_TYPE_L2;
        CTC_ERROR_RETURN(_sys_at_hash_get_packet_field_asic(lchip, &cfg_data, 0));
        p_hash_fields->fields0 = cfg_data.fields;
        break;

    case CTC_LB_HASH_PKT_TYPE_IPV4:
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_IP_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            CTC_ERROR_RETURN(_sys_at_hash_get_ipv4_field(lchip, &hash_fields, &cfg_data));
            p_hash_fields->fields0 |= cfg_data.fields;
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_IPV6:
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_IP_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            CTC_ERROR_RETURN(_sys_at_hash_get_ipv6_field(lchip, &hash_fields, &cfg_data));
            p_hash_fields->fields0 |= cfg_data.fields;
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_MPLS: 
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_MPLS_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            CTC_ERROR_RETURN(_sys_at_hash_get_mpls_field(lchip, &hash_fields, &cfg_data));
            p_hash_fields->fields0 |= cfg_data.fields;
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_VXLAN:
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_VXLAN_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            CTC_ERROR_RETURN(_sys_at_hash_get_vxlan_field(lchip, &hash_fields, &cfg_data));
            p_hash_fields->fields0 |= cfg_data.fields;
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_GRE:
        sub_type_end = (0 == hash_fields.sub_type) ? SYS_HASH_GRE_SUB_TYPE_NUM : hash_fields.sub_type + 1;
        for (; hash_fields.sub_type<sub_type_end; hash_fields.sub_type++)
        {
            sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
            cfg_data.sel_id = hash_fields.sel_grp_id;
            CTC_ERROR_RETURN(_sys_at_hash_get_gre_field(lchip, &hash_fields, &cfg_data));
            p_hash_fields->fields0 |= cfg_data.fields;
        }
        break;

    case CTC_LB_HASH_PKT_TYPE_TRILL:
        sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
        cfg_data.sel_id = hash_fields.sel_grp_id;
        CTC_ERROR_RETURN(_sys_at_hash_get_trill_field(lchip, &hash_fields, &cfg_data));
        p_hash_fields->fields0 |= cfg_data.fields;
        break;

    case CTC_LB_HASH_PKT_TYPE_FCOE:
    {
        uint8 is_outer_l2 = 0;
        sal_memset(&cfg_data, 0, sizeof(sys_hash_cfg_data_t));
        CTC_ERROR_RETURN(_sys_at_hash_get_l2_template_en(lchip, p_hash_fields->sel_grp_id, SYS_HASH_FWD_TYPE_FCOE, &is_outer_l2));
        cfg_data.sel_id = hash_fields.sel_grp_id;
        cfg_data.profile_type[cfg_data.type_cnt] = SYS_HASH_FWD_TYPE_FCOE;
        cfg_data.field_type[cfg_data.type_cnt++] = is_outer_l2 ? SYS_HASH_FIELD_TYPE_L2 : SYS_HASH_FIELD_TYPE_FCOE;
        CTC_ERROR_RETURN(_sys_at_hash_get_packet_field_asic(lchip, &cfg_data, 0));
        p_hash_fields->fields0 |= cfg_data.fields;
        break;
    }

    default:
        return CTC_E_INVALID_PARAM;
        break;
    }

    return CTC_E_NONE;
}

int32
sys_at_hash_get_field(uint8 lchip, void* p_hash_fields)
{
    ctc_lb_hash_fields_t* p_lb_hash_fields = (ctc_lb_hash_fields_t*)p_hash_fields;
    ctc_lb_hash_fields_t lb_hash_fields_tmp;
    ctc_lb_hash_selector_ext_t lb_hash_selector_ext;
    uint8 merge_en = 0;
    uint8 sel_grp_id = p_lb_hash_fields->sel_grp_id;

    /* 4 selector mode do not support merge */
    if ((0 != p_lb_hash_fields->fields1) && SYS_LB_HASH_IS_COMPATIBLE_MODE)
    {
        return CTC_E_NOT_SUPPORT;
    }

    /* odd selector do not support fields1 */
    if ((0 != p_lb_hash_fields->fields1) && (0 != sel_grp_id % 2))
    {
        return CTC_E_INVALID_CONFIG;
    }
    
    /* do not support get second selector when enable merge */
    if ((MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) != SYS_USW_LB_HASH_CTL_NUM))
    {
        /* get merge status */
        sal_memset(&lb_hash_selector_ext, 0, sizeof(ctc_lb_hash_selector_ext_t));
        lb_hash_selector_ext.sel_grp_id = MCHIP_CAP(SYS_CAP_LB_HASH_SELECT_NUM) + sel_grp_id / 2;
        CTC_ERROR_RETURN(sys_at_hash_get_selector_ext(lchip, &lb_hash_selector_ext));
        merge_en = CTC_IS_BIT_SET(lb_hash_selector_ext.sel_grp_bitmap, sel_grp_id);
    }


    /* get fields0 */
    CTC_ERROR_RETURN(_sys_at_hash_get_packet_field(lchip, p_lb_hash_fields));
    
    sal_memset(&lb_hash_fields_tmp, 0, sizeof(ctc_lb_hash_fields_t));
    if (merge_en)
    {
        /* get fields1 */
        lb_hash_fields_tmp.sel_grp_id = sel_grp_id + 1;
        lb_hash_fields_tmp.packet_type = p_lb_hash_fields->packet_type;
        CTC_ERROR_RETURN(_sys_at_hash_get_packet_field(lchip, &lb_hash_fields_tmp));
        p_lb_hash_fields->fields1 = lb_hash_fields_tmp.fields0;
    }

    return CTC_E_NONE;
}

#endif
